﻿using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.Practices.Unity;
using XamlaApp.Helpers;
using XamlaApp.Interfaces;
using XamlaApp.Model.Compression;
using XamlaApp.ViewModels;
using XamlaApp.Views.ElementViews;
using XamlaApp.Views.UIViews;
using myOSity.API;

namespace XamlaApp.Model
{
    public sealed class XamlaModel
    {
        /// <summary>
        /// Receives injected singleton of the MainUIViewModel
        /// </summary>
        [Dependency]
        public MainUIViewModel VM
        {
            get;
            set;
        }

        internal MoRuntimeAPI MoApi { get; set; }
        internal Guid MoWindowID { get; set; }

        #region Keyboard Event Handlers
        //myOSity keyboard event handlers
        internal void ApiWindowKeyUp(object sender, MoWindowKeyEventArgs e)
        {
            ProcessKeyUp(e.KeyPressed);
        }
        internal void ApiWindowKeyDown(object sender, MoWindowKeyEventArgs e)
        {
            ProcessKeyDown(e.KeyPressed);
        }

        //Application keyboard event handlers
        /// <summary>
        /// Handles key up events
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        internal void XamlaStage_KeyUp(object sender, KeyEventArgs e)
        {
            ProcessKeyUp(e.Key);
        }

        /// <summary>
        /// Handles Cut/Copy/Paste Keyboard events
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        internal void XamlaStage_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Ctrl) return; //ignore ctrl
            ProcessKeyDown(e.Key);
        }

        private void ProcessKeyDown(Key e)
        {
            switch (e)
            {
                case Key.C:
                    if (IsCtrlKeyPressed() && VM.CurrentElement != null)
                    {
                        DoCopy();
                    }
                    break;
                case Key.X:
                    if (IsCtrlKeyPressed() && VM.CurrentElement != null)
                    {
                        DoCut();
                    }
                    break;
                case Key.V:
                    if (IsCtrlKeyPressed() && VM.CutCopyBuffer != null)
                    {
                        DoPaste();
                    }
                    break;
            }
        }

        private void ProcessKeyUp(Key e)
        {
            switch (e)
            {
                case Key.Delete:
                    VM.DeleteCurrentElement();
                    break;

            }
        }
        #endregion

        /// <summary>
        /// Places the current element into the cut/copy buffer and then removes it from the stage.
        /// </summary>
        public void DoCut()
        {
            VM.CutCopyBuffer = VM.CurrentElement;
            VM.CutCopyBufferOccupied = true;
            VM.DeleteCurrentElement();
        }

        /// <summary>
        /// Places the current element into the cut/copy buffer.
        /// </summary>
        public void DoCopy()
        {
            VM.CutCopyBuffer = VM.CurrentElement;
            VM.CutCopyBufferOccupied = true;
        }

        /// <summary>
        /// Performs a paste operation on the element current occupying the cut/copy buffer.
        /// </summary>
        public void DoPaste()
        {
            ((IXamlaElement)VM.CutCopyBuffer).ViewModel.PreSerializeActions();
            var text = JSONHelper.ConvertObjectToJsonString(((IXamlaElement)VM.CutCopyBuffer).ViewModel);
            var myType = Container.AppContainer.Resolve(VM.CutCopyBuffer.GetType());

            switch (myType.GetType().Name)
            {
                case "TextBlockView":
                    ((TextBlockView)myType).VM = JSONHelper.ConvertJsonStringToObject<TextBlockViewModel>(text);
                    break;
                case "EllipseView":
                    ((EllipseView)myType).VM = JSONHelper.ConvertJsonStringToObject<EllipseViewModel>(text);
                    break;
                case "RectangleView":
                    ((RectangleView)myType).VM = JSONHelper.ConvertJsonStringToObject<RectangleViewModel>(text);
                    break;
                case "BorderView":
                    ((BorderView)myType).VM = JSONHelper.ConvertJsonStringToObject<BorderViewModel>(text);
                    break;
                case "ImageView":
                    ((ImageView)myType).VM = JSONHelper.ConvertJsonStringToObject<ImageViewModel>(text);
                    break;
                default:
                    return;
            }
            ((IXamlaElement)myType).ViewModel.PostDeserializeActions();
            VM.StageView.AddElement(myType as UserControl);
            VM.PropertyView = ((IXamlaElement)myType).Propertyview;
            ((FrameworkElement)myType).MouseLeftButtonUp += VM.Element_MouseLeftButtonUp;
            VM.CurrentElement = myType as UserControl;
            ((FrameworkElement)myType).Loaded += PastedElement_Loaded;
        }
        private void PastedElement_Loaded(object sender, RoutedEventArgs e)
        {
            var el = (IXamlaElement)sender;
            //we call this in the loaded event of the CurrentElement so that we ensure that
            //the element is within the Canvas visual tree
            el.ViewModel.Top = 20;
            el.ViewModel.Left = 20;
            el.ViewModel.RotationXAmount = el.ViewModel.RotationXAmount;
            el.ViewModel.RotationYAmount = el.ViewModel.RotationYAmount;
            el.ViewModel.RotationZAmount = el.ViewModel.RotationZAmount;
            VM.CurrentElement.Loaded -= PastedElement_Loaded;
        }

        /// <summary>
        /// Transforms the current project into a zipped byte[], which can then be written out to a file
        /// during an SaveFileDialog interaction.
        /// </summary>
        /// <returns></returns>
        public byte[] SaveProject()
        {
            var zio = new XamlaCompression();
            var q = from e in VM.StageView.Children
                    where !(e is ControlEditorHandle) && !(e is ControlEditorView) && e is IXamlaElement
                    select e as IXamlaElement;


            return zio.CompressProject(q);
        }

        /// <summary>
        /// Transforms a zipped byte[] into a project.
        /// </summary>
        /// <param name="file"></param>
        public void LoadProject(byte[] file)
        {
            using (var zio = new XamlaCompression())
            {
                var elementList = zio.UnCompressProject(file);
                if (!elementList.Any()) return;
                InitializeApplication();
                foreach (var e in elementList)
                {
                    VM.StageView.AddElement(e as UserControl);
                    VM.PropertyView = e.Propertyview;
                    ((FrameworkElement)e).MouseLeftButtonUp += VM.Element_MouseLeftButtonUp;
                    ((FrameworkElement)e).Loaded += LoadedElementLoaded;
                }
            }
        }
        public void LoadProject(Stream fileStream)
        {
            var file = new byte[fileStream.Length];
            fileStream.Read(file, 0, (int)fileStream.Length);
            LoadProject(file);
        }

        private static void LoadedElementLoaded(object sender, RoutedEventArgs e)
        {
            //we call this in the loaded event of the loaded element so that we ensure that
            //the element is within the Canvas visual tree
            var el = (IXamlaElement)sender;

            el.ViewModel.Top = el.ViewModel.Top;
            el.ViewModel.Left = el.ViewModel.Left;
            el.ViewModel.RotationXAmount = el.ViewModel.RotationXAmount;
            el.ViewModel.RotationYAmount = el.ViewModel.RotationYAmount;
            el.ViewModel.RotationZAmount = el.ViewModel.RotationZAmount;
            ((FrameworkElement)el).Loaded -= LoadedElementLoaded;
        }

        /// <summary>
        /// Returns true of control key is currently pressed.
        /// </summary>
        /// <returns></returns>
        private static bool IsCtrlKeyPressed()
        {
            var keys = Keyboard.Modifiers;
            return ((keys & ModifierKeys.Control) != 0);
        }

        /// <summary>
        /// Addes a new element to the stage.
        /// </summary>
        /// <param name="elementTypeToAdd">The Type of element to add (view Type).</param>
        internal void AddElement(Type elementTypeToAdd)
        {
            //get a new instance of the type from the container
            var element = (IXamlaElement)Container.AppContainer.Resolve(elementTypeToAdd);
            if (element == null) return;
            if (!(element is FrameworkElement)) throw new ArgumentException("element must derive from FrameworkElement");

            element.ViewModel.ControlName = string.Format("[{0}]", element.GetType().Name.Replace("View", ""));

            VM.StageView.AddElement(element as UserControl);
            ((FrameworkElement)element).Loaded += NewElementLoaded;
            VM.PropertyView = element.Propertyview;
            ((FrameworkElement)element).MouseLeftButtonUp += VM.Element_MouseLeftButtonUp;

            //not null if running in myosity
            if (MoApi != null)
            {
                //register the new element to participate in window focus, so that when a user clicks on it, myOSity will move focus to the
                //window it is running in.  Registration will automatically register child elements, if any.
                MoApi.RegisterAsWindowFocusParticipant(MoWindowID, ((FrameworkElement)element));
            }

            VM.CurrentElement = element as UserControl;
        }
        private static void NewElementLoaded(object sender, RoutedEventArgs e)
        {
            var element = (IXamlaElement)sender;
            element.ViewModel.Top = 20;
            element.ViewModel.Left = 20;
            ((FrameworkElement)element).Loaded -= NewElementLoaded;
        }

        /// <summary>
        /// Resets the application to it's original state
        /// </summary>
        internal void InitializeApplication()
        {
            VM.ElementInfoList.Clear();
            VM.CurrentElement = null;
            VM.PropertyView = null;
            VM.CutCopyBuffer = null;
            VM.StageView.RemoveAllElements();
        }


    }
}
